package com.damon.thread;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.SocketException;

import org.apache.commons.codec.binary.Hex;

import com.damon.Entity.WcsDvcEntity;
import com.damon.log.WcsLog;
import com.damon.utils.GlobalOjbectUtil;
import com.damon.utils.LogUtil;
import com.damon.utils.PropertiesUtil;
import com.damon.utils.ThreadPoolUtil;

/**
 * ps：建立每个单独的socket的线程，对应每一个pcl设备
 * 
 * @author 风蚕
 *
 */
public class SocketThread extends Thread {
	private WcsLog logger;
	
	private Socket socket;
	
	
	
	private WcsDvcEntity wcsDvcEntity;
	private String ip;
	private int port;
	private String dvcNo;
	private String msgStart;// 报文头
	private String msgEnd;// 报文尾
	private Boolean socketIsLive = false;// socket是否连接。
	private StringBuffer suffix = new StringBuffer();// 一次is流中最后一个完整报文后的报文
	
	private int bufferSize;// 每次读取的字节数
	
	private int count;// 该设备处理数据计数
	private long lastHeartbeatTime;//记录最后一次接收报文的时间

	public SocketThread(WcsDvcEntity wcs) {
		this.wcsDvcEntity = wcs;
		this.ip = wcs.getDvcIp();
		this.port = Integer.valueOf(wcs.getPort());
		this.dvcNo = wcs.getDvcNo();
		this.msgStart = wcs.getRevBegin();
		this.msgEnd = wcs.getRevEnd();
		this.logger = LogUtil.getLogger(SocketThread.class, wcs);
		this.bufferSize = Integer.valueOf(PropertiesUtil.getPropertiesMap().get("bytes"));//每次读取的字节数
		this.count = 0;
		this.lastHeartbeatTime = System.currentTimeMillis();
		this.socket = null;
		
		
//		GlobalOjbectUtil.addSocketIsLive(wcsDvcEntity, false);
	}

	@Override
//	@SneakyThrows
	public void run() {
		
		
		//首次创建socket
//		try {
//			logger.info("开始与" + ip + ":" + port + "，创建连接。。。");
//			socket = new Socket(ip, port);
//			/*
//			 * 	添加设备号应该保持同步，因为同时可能有删除设备号的操作。
//			 */
//			synchronized (GlobalOjbectUtil.getDvcList()) {
//				GlobalOjbectUtil.addDvcNo(wcsDvcEntity.getDvcNo());
//			}
//			socketIsLive = true;
//			logger.info("与" + ip + ":" + port + "，连接成功！！");
//		} catch (Exception e) {
//			logger.info("与" + ip + ":" + port + "，创建socket连接失败,确认服务端是否开启！");
//			e.printStackTrace();
//		}
		//创建心跳线程
//		new HeartbeatThread(wcsDvcEntity, heartbeatTime, socket,socket.getRemoteSocketAddress()).start();
		new HeartThread().start();
		/*
		 * 	注意：不能写一段没有任何时间消耗的死循环，这种写法会导致socket无法重连
		 */
		while (true) {
			try {
				while(socketIsLive == false) {
						Thread.sleep(2000);
				}
				InputStream is = null;
				OutputStream os = null;
				
				is = socket.getInputStream();
				os = socket.getOutputStream();
				int length = -2;
				// 接收从is中读取的字节数组
				byte[] receiveBytes = new byte[bufferSize];
				/*
				 *	如果服务端关闭tcp连接，输入流能正常读取到-1，程序无异常。
				 *	如果主动执行socket.close(),is.read()方法会报出抛出异常，因为socket closed.
				 */
				while ((length = is.read(receiveBytes)) != -1) {
					long readStartTime = System.currentTimeMillis();
					lastHeartbeatTime = System.currentTimeMillis();
					byte[] cutZeroBytes = new byte[length];// 存放去除多余零后的字节数组
					/*
					 * 无法确定socket服务端发送的具体报文大小，字节数组必须尽可能的大。因此字节数组末尾会产生多余的0。
					 */
					// 字节数组去零
					System.arraycopy(receiveBytes, 0, cutZeroBytes, 0, length);
					String receiveHexStr = Hex.encodeHexString(cutZeroBytes).toUpperCase();// 字节数组转大写16进制
					logger.info("读取的报文："+receiveHexStr);
					// 分析报文
					receiveHexStr = analyzeMsg(receiveHexStr);
					logger.info("进存储过程的报文："+receiveHexStr);
					//在线程池中调用存储过程
					
					if (!"".equals(receiveHexStr)) {
						Thread runThread = new ProcedureThread(wcsDvcEntity, os, receiveHexStr,readStartTime);
						ThreadPoolUtil.getExecutor().execute(runThread);
						count++;
						GlobalOjbectUtil.addDvcCountToMap(wcsDvcEntity.getDvcNo(), count);
					}
					//重置数组，可以不重置，因为会按读取长度截取。
					receiveBytes = new byte[bufferSize];
				}
				logger.info(ip + ":" + port +"，服务端断开！");
			} catch (SocketException e) {
				logger.error(ip + ":" + port +"，连接断开,提示："+e.getMessage(),e);
				e.printStackTrace();
			} catch (IOException e) {
				logger.error("处理socket报文时发生异常，异常信息："+e.getMessage(),e);
				e.printStackTrace();
			} catch (InterruptedException e) {
				logger.error("线程阻塞产生的错误，异常信息："+e.getMessage(),e);
				e.printStackTrace();
			} finally {
				try {
					if(socketIsLive == true) {
						socketIsLive = false;
						//将设备号移除
						synchronized (GlobalOjbectUtil.getDvcList()) {
							GlobalOjbectUtil.removeDvcNo(wcsDvcEntity.getDvcNo());
						}
						socket.close();
//						is.close();
//						os.close();
					}
				} catch (Exception e) {
					e.printStackTrace();
				}
			}

		}
	}

	/**
	 * Ps:通过上一次留下的报文suffix和接收的报文，判断报文的完整程度
	 * 
	 * @param msg
	 * @return
	 */
	public String analyzeMsg(String msg) {
		String receiveHexStr = msg;
		/*
		 * 对接收报文的处理逻辑： 
		 * 1、如果该报文是个首尾完整的报文，直接传入储存过程，然后把suffix设置为空。
		 * 2、如果该报文有头无尾，则把suffix设置为空，然后把报文存储到suffix里。
		 * 3、如果该报文无头有尾，先查看suffix是否为空，不为空，拼接后报文，传入存储过程；为空，丢弃报文。
		 * 4、如果该报文件无头无尾，先查看suffix是否为空，不为空，拼接报文，保存；为空，丢弃报文。
		 * 总结：逻辑保证suffix里存储的报文数据为空或是有头无尾的状态。
		 */
		if (receiveHexStr.startsWith(msgStart) && receiveHexStr.endsWith(msgEnd)) {
//			System.out.println("有头有尾");
			// 一个完整报文
			suffix.setLength(0); // 清空
		} else if (receiveHexStr.startsWith(msgStart) && !receiveHexStr.endsWith(msgEnd)) {
//			System.out.println("有头无尾");
			suffix.setLength(0);
			suffix.append(receiveHexStr);
			receiveHexStr = "";
		} else if (!receiveHexStr.startsWith(msgStart) && receiveHexStr.endsWith(msgEnd)) {
//			System.out.println("无头有尾");
			if ("".equals(suffix.toString())) {
				receiveHexStr = "";
			} else {
				// 一个完整报文
				receiveHexStr = suffix.append(receiveHexStr).toString();
				suffix.setLength(0);
			}
		} else {
//			System.out.println("无头无尾");
			if (!"".equals(suffix.toString())) {
				receiveHexStr = suffix.append(receiveHexStr).toString();
			}
			receiveHexStr = "";
		}
		return receiveHexStr;
	}
	
	
	/**
	 *	心跳线程的主要功能：1、心跳超时重连。2、检测服务端断开时立刻尝试重连。
	 *	执行逻辑：
	 *		1、首先判断心跳是否超时，如果超时：
	 *				socketIsLive是true，则先断开在重连；
	 *				socketIsLive是false，则重新连接；
	 *		2、如果没有超时：
	 *				socketIsLive是true，不做改变；
	 *				socketIsLive是false，则重新连接；
	 *		
	 *  @author 风蚕
	 */
	class HeartThread extends Thread{
		private static final long MAX_HEARTBEAT_TIME = 30*1000;//最大心跳时间
		private static final int HEARTBEAT_TIME = 5000;//检测心跳的间隔时间

		@Override
		public void run() {

			while (true) {
				try {
//					Thread.sleep(HEARTBEAT_TIME);
					long time = System.currentTimeMillis() - lastHeartbeatTime;//当前时间与最后一次心跳的间隔时间
					
					//是否显示心跳日志
					boolean showHeartbeatLog = GlobalOjbectUtil.getWindow().getShowHeartbeatLogCheckBox().isSelected();
					if(showHeartbeatLog) {
						logger.info(ip + ":" + port + "，距离上一次收到心跳时间：" + time+"ms!");
					}
					
					boolean isHeartbeatTimeOut = time >= MAX_HEARTBEAT_TIME;//心跳是否超时
					
					//是否开启心跳机制
					boolean heartbeatCheck = GlobalOjbectUtil.getWindow().getHeartbeatCheckBox().isSelected();
					if(heartbeatCheck == false) {//如果心跳机制没被勾选，总是设置心跳未超时
						isHeartbeatTimeOut = false;
					}
					if (isHeartbeatTimeOut) {//心跳已超时
						if(socketIsLive == true) {//心跳已超时--程序中连接未断开
							logger.info(ip + ":" + port + "，心跳超时, "+time+" > "+MAX_HEARTBEAT_TIME+" ，设备处于连接中，即将断开重连！");
							socketIsLive = false;
							socket.close();
							GlobalOjbectUtil.removeDvcNo(dvcNo);
							logger.info(ip + ":" + port + "，心跳超时重连，主动断开连接！");
							socket = new Socket(ip, port);//如果连接失败，跳到catch
							socketIsLive = true;
							GlobalOjbectUtil.addDvcNo(dvcNo);
							
							lastHeartbeatTime = System.currentTimeMillis();
							logger.info(ip + ":" + port + "，心跳超时重连，连接成功！\n");
						}else {//心跳已超时--程序中连接已断开
							if (socketIsLive == false) {
								logger.info(ip + ":" + port + "，心跳超时, "+time+" > "+MAX_HEARTBEAT_TIME+" ，设备未连接，开始重连！");
//								logger.error(ip + ":" + port + "，设备已断开，尝试连接！");
								socket = new Socket(ip, port);
								socketIsLive = true;
								GlobalOjbectUtil.addDvcNo(dvcNo);
								lastHeartbeatTime = System.currentTimeMillis();
								logger.info(ip + ":" + port + "，连接成功！\n");
							}
						}
					}else {//心跳未超时
						if (socketIsLive == false) {//程序中连接已断开
							logger.info(ip + ":" + port + "，设备未连接，开始连接！");
							socket = new Socket(ip, port);
							socketIsLive = true;
							GlobalOjbectUtil.addDvcNo(dvcNo);
							lastHeartbeatTime = System.currentTimeMillis();
							logger.info(ip + ":" + port + "，连接成功！\n");
						}
					}
				} catch (Exception e) {
					logger.error(ip + ":" + port + "，连接失败，检查服务端是否开启！！\n",e);
					e.printStackTrace();
				}
				try {
					Thread.sleep(HEARTBEAT_TIME);
				} catch (InterruptedException e) {
					logger.error("线程睡眠错误！！！\n",e);
					e.printStackTrace();
				}
			}
		}
	}
}